﻿using Blm.Utils.Extensions;
using Blm.VisionCloud.FrameWork.Cached;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using VisionCloud.Domain.Common;
using VisionCloud.Domain.DtoModel.Sys;
using VisionCloud.Utility;
using VisionSurface.Service.Sys;
using VisionSurface.Web;
using VisionSurface.Web.Authorize;
using Xunit;
using Xunit.Abstractions;

namespace VisionSurface.WebxUnitTest
{
    /// <summary>
    /// 单元测试基础类
    /// </summary>
    public class TestBase : IClassFixture<CustomWebApplicationFactory<Startup>>
    {
        protected readonly IServiceProvider serviceProvider;
        protected readonly ITestOutputHelper outPut;
        protected readonly HttpClient httpClient;
        protected readonly ICached Cached;
        protected readonly UserService userService;

        private readonly string userName = "13726071926";  // 登录用户名
        private readonly string password = "abc@123456";   // 登录密码

        public TestBase(ITestOutputHelper outPut)
        {
            var app = new WebApplicationFactory<Startup>().WithWebHostBuilder(builder =>
            {
                //设定环境变量
                builder.UseContentRoot("..\\..\\..\\..\\..\\VisionSurface.Web\\bin\\Debug\\netcoreapp3.1");
                builder.UseWebRoot("..\\..\\..\\..\\VisionSurface.Web\\wwwroot");
                builder.UseEnvironment("Local");
            });

            this.outPut = outPut;
            this.serviceProvider = app.Server.Services;
            this.Cached = serviceProvider.GetService<ICached>();
            this.userService = serviceProvider.GetService<UserService>();

            httpClient = app.CreateClient();

            var token = GetToken().Result;
            if (!string.IsNullOrEmpty(token))
                httpClient.DefaultRequestHeaders.Add("Authorization", token);
        }

        public T GetService<T>() => serviceProvider.GetService<T>();

        public T GetRequiredService<T>() => serviceProvider.GetRequiredService<T>();

        /// <summary>
        /// post请求
        /// </summary>
        /// <param name="action">单元测试接口的路由</param>
        /// <param name="body">请求内容</param>
        /// <returns></returns>
        public async Task<ApiResult> PostData(string action, dynamic body)
        {
            var content = new StringContent(JsonConvert.SerializeObject(body));
            content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            var data = await httpClient.PostAsync(action, content);
            var result = await data.Content.ReadAsStringAsync();
            outPut.WriteLine(result);
            var apiReturn = JsonConvert.DeserializeObject<ApiResult>(result);

            return apiReturn;
        }

        /// <summary>
        /// get请求
        /// </summary>
        /// <param name="action">单元测试接口的路由</param>
        /// <returns></returns>
        public async Task<ApiResult> GetData(string action)
        {
            var data = await httpClient.GetAsync(action);
            var result = await data.Content.ReadAsStringAsync();
            outPut.WriteLine(result);
            var apiReturn = JsonConvert.DeserializeObject<ApiResult>(result);

            return apiReturn;
        }

        /// <summary>
        /// 获取Token
        /// </summary>
        /// <returns></returns>
        private async ValueTask<string> GetToken()
        {
            SysUserDto modelDto = new SysUserDto()
            {
                Code = userName,
                Password = password,
                TokenType = TokenTypeEnum.Web
            };

            string clientType = modelDto.TokenType.ToString();
            string logId = $"{clientType}:{modelDto.Code}:127.0.0.1";
            var user = userService.FindUser(modelDto);
            var cacheExp = modelDto.TokenType == TokenTypeEnum.Applet ? TimeSpan.FromDays(30) : TimeSpan.FromDays(1);
            if (user != null)
            {
                user.TokenType = modelDto.TokenType;
                Cached.Set(logId, user, cacheExp, true);
            }

            string tokenStr = await CreateJwtToken(user, logId, modelDto.TokenType, null);

            return $"Bearer {tokenStr}";
        }

        /// <summary>
        /// 创建JwtToken
        /// </summary>
        /// <param name="user"></param>
        /// <param name="logId"></param>
        /// <param name="tokenType"></param>
        /// <param name="jsCode"></param>
        /// <returns></returns>
        private async ValueTask<string> CreateJwtToken(SysUserDto user, string logId, TokenTypeEnum tokenType, string jsCode = null)
        {
            var claims = new[]
            {
                // 注意：拓展参数考虑新旧版本兼容
                new Claim("YunUserGuid", user.YunUserGuid.ToString()),
                new Claim("Name", user.Name),
                new Claim("Code", user.Name),
                new Claim("Role", user.RoleList.IsEmpty() ? string.Empty : string.Join(",",user.RoleList)),
                new Claim("LogId",logId),
                new Claim("Department", user.Department.IsEmpty() ? string.Empty : string.Join(",",user.Department)),
                new Claim("CompanyGuid", user.SysOrgIds.IsEmpty()? string.Empty : string.Join(",",user.SysOrgIds)),
                new Claim("OrgId", user.SysOrgIds.IsEmpty()? string.Empty : string.Join(",",user.SysOrgIds)),
                new Claim("OrgCode", user.SysOrgCodes.IsEmpty() ? string.Empty : string.Join(",",user.SysOrgCodes)),
                new Claim("RoleCode", user.RoleCodeList.IsEmpty() ? string.Empty : string.Join(",",user.RoleCodeList)),
                new Claim("RoleNameList", user.RoleNameList.IsEmpty() ? string.Empty : string.Join(",",user.RoleNameList)),
                new Claim("UserType", user.UserType?.NullToString()),
                new Claim("TokenId", Guid.NewGuid().ToString()),
                new Claim("Company", !string.IsNullOrEmpty(user.Company) ? user.Company : string.Empty),
            };
            if (tokenType == TokenTypeEnum.Applet && !string.IsNullOrEmpty(jsCode))
            {
                var openid = await WeChartApiHelper.GetWxOpenId(jsCode);
                claims = claims.Append(new Claim("WxOpenId", openid)).ToArray();
                var cacheKey = $"Applet:{user.YunUserGuid}:OpenId";
                Cached.Set(cacheKey, openid, TimeSpan.FromDays(30), true);
            }
            var tokenExp = tokenType == TokenTypeEnum.Applet ? DateTime.Now.AddDays(30) : AuthCore.GetExpires();
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthCore.SecurityKey));
            var token = new JwtSecurityToken(
                //颁发者
                issuer: AuthCore.Domain,
                //接收者
                audience: Guid.NewGuid().ToString(), //todo 这边可以优化
                expires: tokenExp, //过期时间
                signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256), //签名证书
                claims: claims//自定义参数
            );
            var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
            return tokenStr;
        }
    }
}
